﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.IO;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Input;
using System.Xml.Linq;
using MathematicallySafe.HeadCounter.Common;
using MathematicallySafe.HeadCounter.Data;
using MathematicallySafe.HeadCounter.Dialogs;
using HeadCounter.Armourer;
using MathematicallySafe.HeadCounter.Commands;
using MathematicallySafe.HeadCounter.Services;
using StructureMap;

namespace MathematicallySafe.HeadCounter.ViewModels
{
    public class RaidImportViewModel : ViewModelBase
    {
        private readonly IArmouryService _armouryService = ObjectFactory.GetInstance<IArmouryService>();

        private const string HEADCOUNT_MARKER_STRING = @"<raid generatedFrom=";

        private HeadCountRaidViewModel _headCountRaid;
        private HeadCountRaidImporter _importer;
        private XElement _raidXml;
        private bool _armouryDataDownloading;

        // todo this lets the view model know when it's view is visible, which should be fixed I guess.
        private bool _isViewInScope;

        public HeadCountRaidViewModel HeadCountRaid
        {
            get { return _headCountRaid; }
            set
            {
                _headCountRaid = value;
                _importer = new HeadCountRaidImporter(_headCountRaid);

                OnPropertyChanged("HeadCountRaid");
                OnPropertyChanged("IsImportingRaid");
                OnPropertyChanged("ValidationMessage");
                OnPropertyChanged("IsRaidValid");
            }
        }

        protected override void SubscribeEvents()
        {
            _armouryService.ArmouryServiceProgressChanged += ArmouryServiceProgressChanged;
            _armouryService.ArmouryServiceCompleted += ArmouryServiceCompleted;
        }

        protected override void UnsubscribeEvents()
        {
            _armouryService.ArmouryServiceProgressChanged -= ArmouryServiceProgressChanged;
            _armouryService.ArmouryServiceCompleted -= ArmouryServiceCompleted;
        }

        void ArmouryServiceProgressChanged(object sender, ProgressChangedEventArgs e)
        {
            ArmouryDataDownloading = true;
        }

        void ArmouryServiceCompleted(object sender, RunWorkerCompletedEventArgs e)
        {
            ArmouryDataDownloading = false;
            CommandManager.InvalidateRequerySuggested();
        }

        private HeadCountRaidImporter Importer
        {
            get { return _importer; }
        }

        public bool IsRaidValid
        {
            get { return HeadCountRaid != null ? HeadCountRaid.IsValid : false; }
        }

        public string ValidationMessage
        {
            get { return HeadCountRaid != null ? HeadCountRaid.ValidationMessage : null; }
        }

        public bool IsImportingRaid
        {
            get { return _headCountRaid != null; }
        }

        public bool IsViewInScope
        {
            get { return _isViewInScope; }
            set
            {
                _isViewInScope = value;
                OnPropertyChanged("IsViewInScope");
            }
        }

        public bool ArmouryDataDownloading
        {
            get { return _armouryDataDownloading; }
            set
            {
                _armouryDataDownloading = value;
                OnPropertyChanged("ArmouryDataDownloading");
            }
        }

        #region Commands

        #region Paste Raid Command

        private DelegateCommand _pasteRaidCommand;

        public ICommand PasteRaidCommand
        {
            get
            {
                if (_pasteRaidCommand == null)
                {
                    _pasteRaidCommand = new DelegateCommand(PasteRaid, CanPasteRaid);
                }

                return _pasteRaidCommand;
            }
        }

        private bool CanPasteRaid()
        {
            return IsViewInScope;
        }

        private void PasteRaid()
        {
            string xml = TryGetXMLFromClipboard();

            if (xml != null)
            {
                BeginRaidImport(xml);
            }
            else
            {
                RaiseError("Clipboard does not contain valid HeadCoung XML");
            }
        }

        private string TryGetXMLFromClipboard()
        {
            string rawXml;

            try
            {
                rawXml = (string) Clipboard.GetData(DataFormats.UnicodeText);

                if (!rawXml.Contains(HEADCOUNT_MARKER_STRING))
                {
                    rawXml = null;
                }
            }
            catch
            {
                rawXml = null;
            }

            return rawXml;
        }

        #endregion

        #region Import Raid Command

        private DelegateCommand _importRaidCommand;

        public ICommand ImportRaidCommand
        {
            get
            {
                if (_importRaidCommand == null)
                {
                    _importRaidCommand = new DelegateCommand(ImportRaid, CanImportRaid);
                }

                return _importRaidCommand;
            }
        }

        private bool CanImportRaid()
        {
            return IsViewInScope && IsImportingRaid && IsRaidValid && !ArmouryDataDownloading;
        }

        private void ImportRaid()
        {
            _importer.ImportRaid();
            EndRaidImport();
        }

        #endregion

        #region Cancel Import Command

        private DelegateCommand _cancelRaidImportCommand;

        public ICommand CancelRaidImportCommand
        {
            get
            {
                if (_cancelRaidImportCommand == null)
                {
                    _cancelRaidImportCommand = new DelegateCommand(CancelRaidImport, CanCancelRaidImport);
                }

                return _cancelRaidImportCommand;
            }
        }

        private bool CanCancelRaidImport()
        {
            return IsViewInScope && IsImportingRaid;
        }

        private void CancelRaidImport()
        {
            _raidXml = null;
            EndRaidImport();
        }

        #endregion

        #endregion

        public void BeginRaidImportFromFile(string filename)
        {
            try
            {
                var xml = ReadXmlFromFile(filename);
                BeginRaidImport(xml);
            }
            catch (Exception)
            {
                RaiseError("Could not load file " + filename);
                EndRaidImport();
            }
        }

        private string ReadXmlFromFile(string filename)
        {
            string content = "";

            try
            {
                // todo make sure Encoding.Default gets all the accented characters correct.
                var sr = new StreamReader(filename, Encoding.Default);
                content = sr.ReadToEnd();
                sr.Close();
            }
            catch (Exception e)
            {
                RaiseError("Error loading file " + filename + " " + e.Message);
            }

            return content;
        }

        public void BeginRaidImport(string xml)
        {
            try
            {
                var hcRaidViewModel = new HeadCountRaidViewModel();
                _raidXml = XElement.Parse(xml);

                hcRaidViewModel.Load(_raidXml);

                HeadCountRaid = hcRaidViewModel;
            }
            catch (Exception e)
            {
                RaiseError("Error parsing XML (" + e.Message + ")");
                EndRaidImport();
            }
        }

        private void EndRaidImport()
        {
            if (_raidXml != null)
            {
                SaveImportedXml();
                _raidXml = null;
            }

            HeadCountRaid = null;
        }

        private void SaveImportedXml()
        {
            EnsureRawXmlDirectoryExists();

            var xmlFileName = Constants.RawXmlDirectory + 
                string.Format("{0}-{1:yyyy-MM-dd_hh-mm-ss}.xml", HeadCountRaid.Zone, HeadCountRaid.RaidDate);

            _raidXml.Save(xmlFileName);
        }

        private void EnsureRawXmlDirectoryExists()
        {
            if (!Directory.Exists(Constants.RawXmlDirectory))
            {
                Directory.CreateDirectory(Constants.RawXmlDirectory);
            }
        }

        private void RaiseError(string errorMessage)
        {
            MessageBox.Show(errorMessage, "Error");
        }
    }
}
